﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using io = System.IO;

namespace editor.app
{
    public class SpriteView
    {
        #region Element
        public abstract class Element
        {
            public virtual void UpdateLayout()
            {
            }
        }
        #endregion

        #region Layer
        public abstract class Layer : Element
        {
            public static Layer NewLayer(SpriteView spriteView, string key, comm.objst.RSprite.ILayer layer)
            {
                var dic = new Dictionary<string, Type>();
                dic.Add(typeof(comm.objst.RSprite.Path).Name, typeof(PathLayer));
                dic.Add(typeof(comm.objst.RSprite.Image).Name, typeof(ImageLayer));
                dic.Add(typeof(comm.objst.RSprite.KeyFrameAnim).Name, typeof(KFAnimLayer));
                if (dic.ContainsKey(layer.GetType().Name))
                {
                    return (Layer)Activator.CreateInstance(dic[layer.GetType().Name], new object[] { spriteView, key, layer });
                }
                return null;
            }

            public bool IsShow
            {
                get { return _root.Visibility == Visibility.Visible; }
                set
                {
                    _root.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
                }
            }

            protected SpriteView _spriteView = null;
            protected comm.objst.RSprite.ILayer _layer = null;
            protected comm.objst.RSprite.Element _el = null;
            protected string _key = "";

            protected double _width = 0;
            protected double _height = 0;
            protected Grid _root = new Grid();
            protected Grid _view = new Grid();
            protected TextBlock _tbName = new TextBlock { FontSize = 12, FontFamily = new FontFamily("SimSun") };
            protected Canvas _cvLayer = null;
            protected ContextMenu _contextMenu = null;
            MenuItem _mLayer = null;

            public Action<string, object[]> CmdCallback = null;

            public Layer(SpriteView spriteView, string key, comm.objst.RSprite.ILayer layer, Nullable<Color> color = null, bool canEdit = true)
            {
                _spriteView = spriteView;
                _layer = layer;
                _key = key;
                _el = (comm.objst.RSprite.Element)_layer;
                _cvLayer = _spriteView._mapLayerDic[layer.MapLayerType.ToString()];

                _root.SetZIndex(_layer.ZIndex);
                _cvLayer.Children.Add(_root);

                _root.Children.Add(_view);

                if (_spriteView.IsSpriteDesign)
                {
                    _root.Children.Add(new Border
                    {
                        BorderThickness = new Thickness(1),
                        BorderBrush = new SolidColorBrush(color.HasValue ? color.Value : Color2.White())
                    });
                }
                TextOptions.SetTextFormattingMode(_tbName, TextFormattingMode.Display);
                _root.Children.Add(new Border
                {
                    Child = _tbName,
                    Height = 12,
                    Margin = new Thickness(0, -12, 0, 0),
                    Background = new SolidColorBrush(Color2.White(50)),
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Top
                });

                var dragEl = new Ellipse
                {
                    Margin = new Thickness(0, -CENTER_DOT_SIZE / 2, -CENTER_DOT_SIZE / 2, 0),
                    HorizontalAlignment = HorizontalAlignment.Right,
                    VerticalAlignment = VerticalAlignment.Top,
                    Width = CENTER_DOT_SIZE,
                    Height = CENTER_DOT_SIZE,
                    Fill = new SolidColorBrush(Color2.White(20)),
                    Stroke = new SolidColorBrush(color.HasValue ? color.Value : Color2.White()),
                    StrokeThickness = 1
                };
                dragEl.Drag(Drag);
                if (_spriteView.IsSpriteDesign)
                {
                    _root.Children.Add(dragEl);
                }

                _contextMenu = new ContextMenu { FontSize = 14 };
                TextOptions.SetTextFormattingMode(_contextMenu, TextFormattingMode.Display);
                _mLayer = new MenuItem { Header = "Layers" };

                var mClone = new MenuItem { Header = "同步" };
                mClone.Click += new RoutedEventHandler(mClone_Click);

                _contextMenu.Items.Add(new TextBlock
                {
                    Foreground = new SolidColorBrush(Color2.Black(70)),
                    Text = _el.Name,
                    HorizontalAlignment = HorizontalAlignment.Left
                });

                var mEdit = new MenuItem { Header = "Edit" };
                mEdit.Click += new RoutedEventHandler(mEdit_Click);

                var mDelete = new MenuItem { Header = "Delete" };
                mDelete.Click += new RoutedEventHandler(mDelete_Click);

                if (!(_layer is comm.objst.RSprite.Path) &&
                    !(_layer is LabelLayer.LLayer) &&
                    _spriteView._spriteGroupKey == comm.objst.RSprite.DEFAULT_GROUP_KEY)
                {
                    _contextMenu.Items.Add(_mLayer);
                }
                _contextMenu.Items.Add(new Separator());

                if (_spriteView.IsSpriteDesign)
                {
                    if (_spriteView._spriteGroupKey == comm.objst.RSprite.DEFAULT_GROUP_KEY)
                    {
                        _contextMenu.Items.Add(mClone);
                    }
                    if (canEdit)
                    {
                        _contextMenu.Items.Add(mEdit);
                    }
                    if (_spriteView._spriteGroupKey == comm.objst.RSprite.DEFAULT_GROUP_KEY)
                    {
                        _contextMenu.Items.Add(mDelete);
                    }

                    if (this is PathLayer)
                    {
                        dragEl.ContextMenu = _contextMenu;
                    }
                    else
                    {
                        _root.ContextMenu = _contextMenu;
                    }
                }

                _contextMenu.Opened += new RoutedEventHandler(cm_Opened);
                _root.SizeChanged += new SizeChangedEventHandler(_root_SizeChanged);
            }

            void cm_Opened(object sender, RoutedEventArgs e)
            {
                _mLayer.Items.Clear();
                var layers = comm.objst.DMap.Data.GetObjChooseLayerTypes();
                if (this is PointLayer)
                {
                    layers = comm.objst.DMap.Data.GetAllLayerTypes();
                }
                foreach (var layer in layers)
                {
                    var tb = new TextBlock { Text = layer };
                    if (layer == _layer.MapLayerType.ToString())
                    {
                        tb.FontWeight = FontWeights.Bold;
                    }
                    var mi = new MenuItem { Header = tb, Tag = layer };
                    mi.Click += new RoutedEventHandler(mi_Click);
                    _mLayer.Items.Add(mi);
                }
            }
            void mi_Click(object sender, RoutedEventArgs e)
            {
                var mi = (MenuItem)sender;
                var type = comm.EnumHelper.Parse<comm.objst.MapLayerType>(mi.Tag.ToString());
                Cmd("setLayer", _key, type);
            }
            void mDelete_Click(object sender, RoutedEventArgs e)
            {
                Cmd("delete", _key);
            }
            void mEdit_Click(object sender, RoutedEventArgs e)
            {
                Cmd("edit", _key);
            }
            void _root_SizeChanged(object sender, SizeChangedEventArgs e)
            {
                _width = e.NewSize.Width;
                _height = e.NewSize.Height;
            }
            void mClone_Click(object sender, RoutedEventArgs e)
            {
                Cmd("clone", _key);
            }

            protected void Cmd(string cmd, params object[] args)
            {
                if (CmdCallback != null)
                {
                    CmdCallback(cmd, args);
                }
            }

            void Drag(FrameworkElement sender, MouseEventArgs e, string type, double ox, double oy)
            {
                OnDrag(sender, e, type, ox, oy);
                UpdateLayout();
            }
            protected virtual void OnDrag(FrameworkElement sender, MouseEventArgs e, string type, double ox, double oy)
            {
            }
            public abstract Rect GetRect();
            public override void UpdateLayout()
            {
                var rect = GetRect();
                _root.SetLeftTop(rect.X, rect.Y);
                if (rect.Width > 0)
                {
                    _root.Width = rect.Width;
                }
                if (rect.Height > 0)
                {
                    _root.Height = rect.Height;
                }
            }
        }
        #endregion

        #region PathLayer
        public class PathLayer : Layer
        {
            comm.objst.RSprite.Path _path = null;

            public PathLayer(SpriteView spriteView, string key, comm.objst.RSprite.ILayer layer)
                : base(spriteView, key, layer, Color2.Yellow())
            {
                _path = (comm.objst.RSprite.Path)layer;

                if (_spriteView.IsSpriteDesign)
                {
                    _view.Background = new SolidColorBrush(Color2.Transparent());
                }

                UpdateLayout();

                if (_spriteView.IsSpriteDesign)
                {
                    //_view.MouseLeftButtonDown += new MouseButtonEventHandler(_viewh_MouseLeftButtonDown);
                    _view.MouseRightButtonDown += new MouseButtonEventHandler(_view_MouseRightButtonDown);

                    _view.Drag(Drag);
                }
            }

            void Drag(FrameworkElement sender, MouseEventArgs e, string type, double ox, double oy)
            {
                var pt = e.GetPosition(_view);
                var px = (int)Math.Floor(pt.X / PATH_SIZE);
                var py = (int)Math.Floor(pt.Y / PATH_SIZE);
                if (px >= 0 && py >= 0)
                {
                    var find = false;
                    foreach (var p in _path.PathList)
                    {
                        if (p[0] == px && p[1] == py)
                        {
                            find = true;
                            break;
                        }
                    }
                    if (!find)
                    {
                        Console.WriteLine(find);
                        _path.PathList.Add(new int[] { px, py });
                        UpdateLayout();
                    }
                }
            }

            void _view_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
            {
                var pt = e.GetPosition(_view);
                var px = (int)Math.Floor(pt.X / PATH_SIZE);
                var py = (int)Math.Floor(pt.Y / PATH_SIZE);
                foreach (var p in _path.PathList)
                {
                    if (p[0] == px && p[1] == py)
                    {
                        _path.PathList.Remove(p);
                        UpdateLayout();
                        break;
                    }
                }
            }
            void _viewh_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                var pt = e.GetPosition(_view);
                var px = (int)Math.Floor(pt.X / PATH_SIZE);
                var py = (int)Math.Floor(pt.Y / PATH_SIZE);
                var find = false;
                foreach (var p in _path.PathList)
                {
                    if (p[0] == px && p[1] == py)
                    {
                        find = true;
                        break;
                    }
                }
                if (!find)
                {
                    _path.PathList.Add(new int[] { px, py });
                    UpdateLayout();
                }
            }

            protected override void OnDrag(FrameworkElement sender, MouseEventArgs e, string type, double ox, double oy)
            {
                var pt = e.GetPosition(_cvLayer);
                var px = Math.Max(0, (int)Math.Round((pt.X - ox - _width + CENTER_DOT_SIZE / 2) / PATH_SIZE));
                var py = Math.Max(0, (int)Math.Round((pt.Y - oy + CENTER_DOT_SIZE / 2) / PATH_SIZE));
                _layer.OffsetX = px - _spriteView.LocX;
                _layer.OffsetY = py - _spriteView.LocY;
                base.OnDrag(sender, e, type, ox, oy);
            }
            public override Rect GetRect()
            {
                var x = (_spriteView.LocX + _layer.OffsetX) * PATH_SIZE;
                var y = (_spriteView.LocY + _layer.OffsetY) * PATH_SIZE;

                int maxH = 0, maxW = 0;
                foreach (var p in _path.PathList)
                {
                    maxW = Math.Max(maxW, p[0]);
                    maxH = Math.Max(maxH, p[1]);
                }
                return new Rect
                {
                    X = x,
                    Y = y,
                    Width = Math.Max((maxW + 4) * PATH_SIZE, 4),
                    Height = Math.Max((maxH + 3) * PATH_SIZE, 4)
                };
            }
            public override void UpdateLayout()
            {
                _view.Children.Clear();
                foreach (var p in _path.PathList)
                {
                    _view.Children.Add(new Ellipse
                    {
                        Width = PATH_SIZE,
                        Height = PATH_SIZE,
                        Margin = new Thickness(p[0] * PATH_SIZE, p[1] * PATH_SIZE, 0, 0),
                        Stroke = new SolidColorBrush(Color2.Yellow(50)),
                        StrokeThickness = 1,
                        HorizontalAlignment = HorizontalAlignment.Left,
                        VerticalAlignment = VerticalAlignment.Top
                    });
                }
                _tbName.Text = _el.Name;
                base.UpdateLayout();
            }
        }
        #endregion

        #region ImageLayer
        public class ImageLayer : Layer
        {
            comm.objst.RSprite.Image _image = null;
            int _centreX = 0, _centreY = 0;

            public ImageLayer(SpriteView spriteView, string key, comm.objst.RSprite.ILayer layer)
                : base(spriteView, key, layer, Color2.GreenYellow())
            {
                _spriteView = spriteView;
                _image = (comm.objst.RSprite.Image)layer;

                ReloadImage();

                var mSetFrame = new MenuItem { Header = "选择Frame" };
                mSetFrame.Click += new RoutedEventHandler(mSetFrame_Click);
                _contextMenu.Items.Insert(1, mSetFrame);

                UpdateLayout();
            }

            void mSetFrame_Click(object sender, RoutedEventArgs e)
            {
                var window = new Window { Width = 800, Height = 600, Background = new SolidColorBrush(Color2.Black()) };
                var rimageView = new RImageView();
                rimageView.Load(_spriteView._rpack, _image.ResKey, new int[] { _image.FrameIndex });
                rimageView.OnClickFrameCalback = (frame, x, y) =>
                {
                    _image.FrameIndex = frame;
                    ReloadImage();
                    UpdateLayout();
                    window.Close();
                };
                window.Content = new ScrollViewer
                {
                    Focusable = false,
                    HorizontalScrollBarVisibility = ScrollBarVisibility.Visible,
                    Content = rimageView
                };
                window.LoadConfig1();
                window.ShowDialog();
                rimageView.OnClickFrameCalback = null;
            }

            void ReloadImage()
            {
                var rimage = _spriteView._rpack.GetResource<comm.objst.RImage>(_image.ResKey);
                if (rimage != null)
                {
                    var frame = rimage.GetFrame(_image.FrameIndex);
                    if (frame != null)
                    {
                        _centreX = frame.CentreX;
                        _centreY = frame.CentreY;
                    }
                }

                var cv = RImage.GetFrameCanvas(_spriteView._rpack, _image.ResKey, _image.FrameIndex);
                _view.Children.Clear();
                _view.Children.Add(cv);
            }

            public override Rect GetRect()
            {
                var x = _spriteView.LocX * PATH_SIZE + PATH_SIZE / 2 - _centreX;
                var y = _spriteView.LocY * PATH_SIZE + PATH_SIZE / 2 - _centreY;
                return new Rect { X = x, Y = y };
            }
        }
        #endregion

        #region KFAnimLayer
        public class KFAnimLayer : Layer
        {
            comm.objst.RSprite.KeyFrameAnim _kfanim = null;
            MenuItem _mImages = null;
            int _centreX = 0, _centreY = 0;

            public KFAnimLayer(SpriteView spriteView, string key, comm.objst.RSprite.ILayer layer)
                : base(spriteView, key, layer, Color2.LightPink())
            {
                _spriteView = spriteView;
                _kfanim = (comm.objst.RSprite.KeyFrameAnim)layer;

                var mSetImage = new MenuItem { Header = "Image By GroupName" };
                mSetImage.Click += new RoutedEventHandler(mSetImage_Click);
                _contextMenu.Items.Insert(1, mSetImage);

                _mImages = new MenuItem { Header = "Images" };
                _mImages.Items.Add("load...");
                _mImages.SubmenuOpened += new RoutedEventHandler(_mImages_SubmenuOpened);
                _mImages.SubmenuClosed += new RoutedEventHandler(_mImages_SubmenuClosed);
                _contextMenu.Items.Insert(2, _mImages);

                ReloadKFAnim();
                UpdateLayout();
            }

            void mSetImage_Click(object sender, RoutedEventArgs e)
            {
                _spriteView.ReceiveCmd("setImageByGroupName", _key);
            }
            void _mImages_SubmenuClosed(object sender, RoutedEventArgs e)
            {
                _mImages.Items.Clear();
                _mImages.Items.Add("load...");
            }
            void _mImages_SubmenuOpened(object sender, RoutedEventArgs e)
            {
                _mImages.Items.Clear();
                var rkfa = _spriteView._rpack.GetResource<comm.objst.RKeyFrameAnim>(_kfanim.ResKey);
                if (rkfa != null && rkfa.ImageDic.Count > 0)
                {
                    foreach (var imageKey in rkfa.ImageDic.Keys)
                    {
                        var tb = new TextBlock { Text = rkfa.ImageDic[imageKey].Name };
                        if (imageKey == _kfanim.ImageKey)
                        {
                            tb.FontWeight = FontWeights.Bold;
                        }
                        var mi = new MenuItem { Tag = imageKey, Header = tb };
                        mi.Click += new RoutedEventHandler(mi_Click);
                        _mImages.Items.Add(mi);
                    }
                }
            }
            void mi_Click(object sender, RoutedEventArgs e)
            {
                var mi = (MenuItem)sender;
                _kfanim.ImageKey = mi.Tag.ToString();
                ReloadKFAnim();
                UpdateLayout();
            }

            void ReloadKFAnim()
            {
                _view.Children.Clear();
                var view = new RKFAnimView();
                view.Load(_spriteView._rpack, _kfanim.ResKey, _kfanim.ImageKey);
                _centreX = view.MaxCentreX;
                _centreY = view.MaxCentreY;
                _view.Children.Add(view);
            }

            public override Rect GetRect()
            {
                var x = _spriteView.LocX * PATH_SIZE + PATH_SIZE / 2 - _centreX;
                var y = _spriteView.LocY * PATH_SIZE + PATH_SIZE / 2 - _centreY;
                return new Rect { X = x, Y = y };
            }
        }
        #endregion

        #region LabelLayer
        public class LabelLayer : Layer
        {
            public class LLayer : comm.objst.RSprite.Element, comm.objst.RSprite.ILayer
            {
                public comm.objst.MapLayerType MapLayerType { get; set; }
                public int OffsetX { get; set; }
                public int OffsetY { get; set; }
                public int ZIndex { get; set; }

                public override comm.objst.RSprite.Element CopyToGroupElement(comm.objst.RSprite.Element el)
                {
                    throw new NotImplementedException();
                }
            }

            comm.objst.RSprite.Label _label = null;
            List<PointLayer> _pointList = new List<PointLayer>();

            public LabelLayer(SpriteView spriteView, string key, comm.objst.RSprite.Label label)
                : base(spriteView, key, new LLayer
                {
                    Name = label.Name,
                    MapLayerType = comm.objst.MapLayerType.DESIGN,
                    OffsetX = 0,
                    OffsetY = 0,
                    ZIndex = 0
                })
            {
                _label = label;

                if (_spriteView.IsSpriteDesign)
                {
                    _tbName.Width = 40;

                    var sp = new StackPanel { Orientation = Orientation.Horizontal };
                    var brNewPoint = new Border
                    {
                        CornerRadius = new CornerRadius(2),
                        Background = new SolidColorBrush(Color2.White(50)),
                        BorderBrush = new SolidColorBrush(Color2.White()),
                        BorderThickness = new Thickness(1),
                        Child = new TextBlock { Text = "+Point" }
                    };
                    if (_spriteView._spriteGroupKey == comm.objst.RSprite.DEFAULT_GROUP_KEY)
                    {
                        brNewPoint.MouseLeftButtonDown += new MouseButtonEventHandler(brNewPoint_MouseLeftButtonDown);
                    }
                    else
                    {
                        _root.Opacity = .5;
                    }
                    sp.Children.Add(brNewPoint);
                    _view.Margin = new Thickness(4);
                    _view.Children.Add(sp);
                }
                foreach (var pointKey in _label.PointDic.Keys)
                {
                    var pl = new PointLayer(_spriteView, pointKey, _label.PointDic[pointKey]);
                    _pointList.Add(pl);
                    pl.CmdCallback = ReceiveCmd;
                }
            }

            void brNewPoint_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                var key = comm.GUID.NewString();
                _label.PointDic.Add(key, new comm.objst.RSprite.Point
                {
                    Name = key,
                    MapLayerType = comm.objst.MapLayerType.DESIGN
                });
                _spriteView.ReceiveCmd("fill");
            }

            void ReceiveCmd(string cmd, object[] args)
            {
                if (cmd == "delete")
                {
                    _label.PointDic.Remove(args[0].ToString());
                }
                else if (cmd == "setLayer")
                {
                    _label.PointDic[args[0].ToString()].MapLayerType = (comm.objst.MapLayerType)args[1];
                }
                else if (cmd == "setKey")
                {
                    string key = args[0].ToString(), newKey = args[1].ToString();
                    if (!_label.PointDic.ContainsKey(newKey))
                    {
                        var p = _label.PointDic[key];
                        _label.PointDic.Remove(key);
                        _label.PointDic.Add(newKey, p);
                    }
                }
                else if (cmd == "clone")
                {
                    _spriteView.ReceiveCmd("clone", _key, args[0].ToString());
                }
                _spriteView.ReceiveCmd("fill");
            }

            protected override void OnDrag(FrameworkElement sender, MouseEventArgs e, string type, double ox, double oy)
            {
                if (_spriteView._spriteGroupKey == comm.objst.RSprite.DEFAULT_GROUP_KEY)
                {
                    var pt = e.GetPosition(_cvLayer);
                    var px = (int)(pt.X - ox - _width + CENTER_DOT_SIZE / 2);
                    var py = (int)(pt.Y - oy + CENTER_DOT_SIZE / 2);
                    _label.DesignX = px;
                    _label.DesignY = py;

                    if (type == "up")
                    {
                        _spriteView.ReceiveCmd("labelMove");
                    }
                }
                base.OnDrag(sender, e, type, ox, oy);
            }
            public override Rect GetRect()
            {
                return new Rect { X = _label.DesignX, Y = _label.DesignY };
            }
            public override void UpdateLayout()
            {
                if (_spriteView.IsSpriteDesign)
                {
                    _tbName.Text = _label.Name;
                }
                foreach (var item in _pointList)
                {
                    item.UpdateLayout();
                }
                base.UpdateLayout();
            }
        }
        #endregion

        #region PointLayer
        public class PointLayer : Layer
        {
            comm.objst.RSprite.Point _point = null;
            MenuItem _miKeys = null;

            public PointLayer(SpriteView spriteView, string key, comm.objst.RSprite.Point point)
                : base(spriteView, key, point, Color2.White(30), false)
            {
                _point = point;

                _view.Width = 120;
                _view.Children.Add(new Ellipse
                {
                    Width = 12,
                    Height = 12,
                    Stroke = new SolidColorBrush(Color2.Red()),
                    Fill = new SolidColorBrush(Color2.Red(30)),
                    StrokeThickness = 1,
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Top
                });

                _tbName.Width = 60;

                _miKeys = new MenuItem { Header = "Keys" };
                _miKeys.Items.Add("load...");
                _miKeys.SubmenuOpened += new RoutedEventHandler(_miKeys_SubmenuOpened);
                _miKeys.SubmenuClosed += new RoutedEventHandler(_miKeys_SubmenuClosed);
                if (_spriteView._spriteGroupKey == comm.objst.RSprite.DEFAULT_GROUP_KEY)
                {
                    _contextMenu.Items.Insert(1, _miKeys);
                }

                UpdateLayout();
            }

            void _miKeys_SubmenuClosed(object sender, RoutedEventArgs e)
            {
                _miKeys.Items.Clear();
                _miKeys.Items.Add("load...");
            }
            void _miKeys_SubmenuOpened(object sender, RoutedEventArgs e)
            {
                _miKeys.Items.Clear();
                var types = comm.objst.DObject.Data.GetPointTypes();
                foreach (var type in types)
                {
                    var tb = new TextBlock { Text = type };
                    if (type == _key)
                    {
                        tb.FontWeight = FontWeights.Black;
                    }
                    var mi = new MenuItem { Tag = type, Header = tb };
                    mi.Click += new RoutedEventHandler(mi_Click);
                    _miKeys.Items.Add(mi);
                }
            }
            void mi_Click(object sender, RoutedEventArgs e)
            {
                var mi = (MenuItem)sender;
                Cmd("setKey", _key, mi.Tag.ToString());
            }

            protected override void OnDrag(FrameworkElement sender, MouseEventArgs e, string type, double ox, double oy)
            {
                var pt = e.GetPosition(_cvLayer);
                var px = (int)(pt.X - ox - _width + CENTER_DOT_SIZE / 2);
                var py = (int)(pt.Y - oy + CENTER_DOT_SIZE / 2);
                _layer.OffsetX = px - _spriteView.LocX * PATH_SIZE;
                _layer.OffsetY = py - _spriteView.LocY * PATH_SIZE;

                base.OnDrag(sender, e, type, ox, oy);
            }
            public override Rect GetRect()
            {
                var x = _spriteView.LocX * PATH_SIZE + _layer.OffsetX;
                var y = _spriteView.LocY * PATH_SIZE + _layer.OffsetY;
                return new Rect { X = x, Y = y };
            }
            public override void UpdateLayout()
            {
                _tbName.Text = _key;
                base.UpdateLayout();
            }
        }
        #endregion

        public const int PATH_SIZE = comm.objst.DMap.Data.PATH_SIZE;

        const int CENTER_DOT_SIZE = 20;

        public int CenterSize = 600;

        public int LocX { get { return IsSpriteDesign ? _sprite.DesignX : _locX; } }
        public int LocY { get { return IsSpriteDesign ? _sprite.DesignY : _locY; } }
        public bool IsSpriteDesign { get; private set; }

        int _locX = 0, _locY = 0;
        Dictionary<string, Canvas> _mapLayerDic = null;
        comm.objst.RPack _rpack = null;
        comm.objst.RSprite _sprite = null;
        string _spriteResKey = "";
        string _spriteGroupKey = comm.objst.RSprite.DEFAULT_GROUP_KEY;

        string _key = "";
        comm.objst.DMap.Data.Obj _mapObj = null;

        MenuItem _mGroups = null;
        MenuItem _mActions = null;

        List<Border> _brRangeList = new List<Border>();

        public Action<string, object[]> CmdCallback = null;

        Grid _gridRoot = null;
        Dictionary<string, Element> _elementDic = new Dictionary<string, Element>();

        public SpriteView(Dictionary<string, Canvas> mapLayerDic, comm.objst.RPack rpack, string spriteResKey, bool isSpriteDesign = true, string key = null, comm.objst.DMap.Data.Obj mapObj = null, string spriteGroupKey = comm.objst.RSprite.DEFAULT_GROUP_KEY)
        {
            _mapLayerDic = mapLayerDic;
            _spriteResKey = spriteResKey;
            _spriteGroupKey = spriteGroupKey;
            _rpack = rpack;
            _key = key;
            _mapObj = mapObj;
            IsSpriteDesign = isSpriteDesign;

            if (spriteResKey != null)
            {
                var ss = spriteResKey.Split(':');
                if (ss.Length == 1)
                {
                    _sprite = _rpack.GetResource<comm.objst.RSprite>(spriteResKey);
                }
                else if (ss[0] == "FindName")
                {
                    var sprites = _rpack.FindResource<comm.objst.RSprite>(ss[1]);
                    if (sprites.Length > 0)
                    {
                        _sprite = sprites[0];
                        _spriteResKey = ss[1];
                    }
                }
            }

            if (!isSpriteDesign)
            {
                CenterSize = 120;
            }

            Reload();
        }

        void DragGridCentreLine(FrameworkElement sender, MouseEventArgs e, string type, double ox, double oy)
        {
            e.Handled = true;
            var cvDesign = _mapLayerDic[comm.objst.MapLayerType.DESIGN.ToString()];
            var pt = e.GetPosition(cvDesign);

            var x = (int)Math.Min(Math.Floor(cvDesign.Width / PATH_SIZE) - 1, Math.Max(0, (int)Math.Round((pt.X - ox - CenterSize / 2 - CENTER_DOT_SIZE / 2) / PATH_SIZE)));
            var y = (int)Math.Min(Math.Floor(cvDesign.Height / PATH_SIZE) - 1, Math.Max(0, (int)Math.Round((pt.Y - oy + CENTER_DOT_SIZE / 2) / PATH_SIZE)));
            if (IsSpriteDesign)
            {
                if (_sprite != null)
                {
                    _sprite.DesignX = x;
                    _sprite.DesignY = y;
                }
            }
            else
            {
                ReceiveCmd("move", _key, x, y);
                SetLoc(x, y);
            }
            UpdateLayout();
        }
        void mDelete_Click(object sender, RoutedEventArgs e)
        {
            ReceiveCmd("delete", _key);
        }
        void mEditRPack_Click(object sender, RoutedEventArgs e)
        {
            ReceiveCmd("editRPack", _key);
        }
        void contextMenu_Opened(object sender, RoutedEventArgs e)
        {
            _mGroups.Items.Clear();
            if (_sprite != null)
            {
                foreach (var groupKey in _sprite.GroupDic.Keys)
                {
                    var group = _sprite.GroupDic[groupKey];
                    var tb = new TextBlock { Text = group.Name };
                    if (groupKey == _spriteGroupKey)
                    {
                        tb.FontWeight = FontWeights.Bold;
                    }
                    var mi = new MenuItem { Header = tb, Tag = groupKey };
                    mi.Click += new RoutedEventHandler(miGroup_Click);
                    _mGroups.Items.Add(mi);
                }
            }

            if (_mapObj is comm.objst.DMap.Data.SObj)
            {
                _mActions.Items.Clear();
                var mapObj = (comm.objst.DMap.Data.SObj)_mapObj;
                var objData = App.DefineFile.GetObjectData(mapObj.DObjectId);
                var accKey = _spriteResKey.Substring(mapObj.DObjectId.Length + 1, _spriteResKey.Length - mapObj.DObjectId.Length - 1);
                foreach (var acKey in objData.ActionDic.Keys)
                {
                    var tb = new TextBlock { Text = acKey };
                    if (acKey == accKey)
                    {
                        tb.FontWeight = FontWeights.Bold;
                    }
                    var mi = new MenuItem { Header = tb, Tag = acKey };
                    mi.Click += new RoutedEventHandler(miAction_Click);
                    _mActions.Items.Add(mi);
                }
            }
        }
        void miAction_Click(object sender, RoutedEventArgs e)
        {
            var mi = (MenuItem)sender;
            ReceiveCmd("setAction", _key, mi.Tag.ToString());
        }
        void miGroup_Click(object sender, RoutedEventArgs e)
        {
            var mi = (MenuItem)sender;
            ReceiveCmd("setGroup", _key, mi.Tag.ToString());
        }
        void mCopy_Click(object sender, RoutedEventArgs e)
        {
            ReceiveCmd("copy", _key);
        }

        void ReceiveCmd(string cmd, params object[] args)
        {
            if (CmdCallback != null)
            {
                CmdCallback(cmd, args);
            }
        }

        public void ChooseGroup(string key)
        {
            _spriteGroupKey = key;
            Reload();
        }
        public void SetLoc(int locX, int locY)
        {
            _locX = locX;
            _locY = locY;

            UpdateLayout();
        }
        public void Reload()
        {
            comm.objst.DMap.Data.SObj sobj = null;
            if (_mapObj is comm.objst.DMap.Data.SObj)
            {
                sobj = (comm.objst.DMap.Data.SObj)_mapObj;
            }

            var cvDesign = _mapLayerDic[comm.objst.MapLayerType.DESIGN.ToString()];

            #region CenterLine
            var color = Color2.Red();
            if (_sprite == null)
            {
                color = Color2.GreenYellow();
            }
            _gridRoot = new Grid { Width = CenterSize, Height = CenterSize };
            TextOptions.SetTextFormattingMode(_gridRoot, TextFormattingMode.Display);
            TextBlock.SetFontFamily(_gridRoot, new FontFamily("SimSun"));
            _gridRoot.Children.Add(new Rectangle { Fill = new SolidColorBrush(color), Width = CenterSize, Height = 1 });
            _gridRoot.Children.Add(new Rectangle { Fill = new SolidColorBrush(color), Width = 1, Height = CenterSize });

            _gridRoot.Children.Add(new Ellipse { Width = 5, Height = 5, Fill = new SolidColorBrush(color) });

            var rectDot = new Ellipse
            {
                Margin = new Thickness(0, CenterSize / 2 - CENTER_DOT_SIZE / 2, -CENTER_DOT_SIZE, 0),
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Top,
                Width = CENTER_DOT_SIZE,
                Height = CENTER_DOT_SIZE,
                Fill = new SolidColorBrush(Color2.White(20)),
                Stroke = new SolidColorBrush(color),
                StrokeThickness = 1
            };
            rectDot.MouseEnter += new MouseEventHandler((o, e) =>
            {
                rectDot.Fill = new SolidColorBrush(Color2.White(30));
            });
            rectDot.MouseLeave += new MouseEventHandler((o, e) =>
            {
                rectDot.Fill = new SolidColorBrush(Color2.White(20));
            });

            _gridRoot.Children.Add(rectDot);
            rectDot.Drag(DragGridCentreLine);

            cvDesign.Children.Add(_gridRoot);
            #endregion

            if (sobj != null)
            {
                #region MapEVDic Mark
                foreach (var evKey in sobj.EVDic.Keys)
                {
                    var ev = sobj.EVDic[evKey];
                    if (ev.Value is comm.lib.MapRange.Round)
                    {
                        #region MapRange.Round
                        var roundRange = (comm.lib.MapRange.Round)ev.Value;
                        var radius = roundRange.Radius;
                        var br = new Border
                        {
                            Tag = roundRange,
                            Width = (radius * 2 + 1) * PATH_SIZE,
                            Height = (radius * 2 + 1) * PATH_SIZE,
                            CornerRadius = new CornerRadius((radius * 2 + 1) * PATH_SIZE),
                            BorderBrush = new SolidColorBrush(Color2.White()),
                            BorderThickness = new Thickness(1)
                        };
                        cvDesign.Children.Add(br);
                        _brRangeList.Add(br);
                        #endregion
                    }
                    else if (ev.Value is comm.lib.MapEvent)
                    {
                        #region MapEvent
                        var mev = (comm.lib.MapEvent)ev.Value;
                        _gridRoot.Children.Add(new Border
                        {
                            Padding = new Thickness(2, 0, 2, 0),
                            VerticalAlignment = VerticalAlignment.Bottom,
                            HorizontalAlignment = HorizontalAlignment.Center,
                            Background = new SolidColorBrush(Color2.Black(50)),
                            Child = new TextBlock
                            {
                                Text = mev.ToString(),
                                Foreground = new SolidColorBrush(Color2.DeepSkyBlue()),
                                FontSize = 12
                            }
                        });
                        #endregion
                    }
                }
                #endregion
            }

            #region contextMenu
            if (!IsSpriteDesign)
            {
                var contextMenu = new ContextMenu { FontSize = 14 };
                TextOptions.SetTextFormattingMode(contextMenu, TextFormattingMode.Display);

                contextMenu.Opened += new RoutedEventHandler(contextMenu_Opened);

                #region name
                contextMenu.Items.Add(new TextBlock
                {
                    Text = (sobj != null ? "(" + ((comm.objst.DMap.Data.SObj)_mapObj).DObjectId + ")" : "") + _mapObj.Name,
                    Width = 120,
                    Foreground = new SolidColorBrush(Color2.Black(70)),
                    HorizontalAlignment = HorizontalAlignment.Left
                });
                #endregion

                #region mEditMapObj
                var mEditMapObj = new MenuItem { Header = "Edit MapObj" };
                mEditMapObj.Click += new RoutedEventHandler((o, e) =>
                {
                    ReceiveCmd("editMapObj", _key);
                });
                contextMenu.Items.Add(mEditMapObj);
                #endregion

                #region mCopyMapObj
                var mCopyMapObj = new MenuItem { Header = "Copy MapObj" };
                contextMenu.Items.Add(mCopyMapObj);
                mCopyMapObj.Click += new RoutedEventHandler(mCopy_Click);
                #endregion

                #region mSetExternal
                var sp = new StackPanel { Orientation = Orientation.Horizontal };
                sp.Children.Add(new TextBlock { Text = "IsExternal", Margin = new Thickness(0, 0, 4, 0) });
                sp.Children.Add(new Border
                {
                    Width = 12,
                    Height = 12,
                    CornerRadius = new CornerRadius(12),
                    BorderThickness = new Thickness(2),
                    BorderBrush = new SolidColorBrush(_mapObj.IsExternal ? Color2.Black() : Color2.Black(20))
                });
                var mSetExternal = new MenuItem { Header = sp };
                mSetExternal.Click += new RoutedEventHandler((o, e) =>
                {
                    _mapObj.IsExternal = !_mapObj.IsExternal;
                    ReceiveCmd("fill");
                });
                contextMenu.Items.Add(mSetExternal);
                #endregion

                contextMenu.Items.Add(new Separator());

                if (sobj != null)
                {
                    _mActions = new MenuItem { Header = "Actions" };
                    contextMenu.Items.Add(_mActions);
                }

                _mGroups = new MenuItem { Header = "Groups" };
                contextMenu.Items.Add(_mGroups);

                contextMenu.Items.Add(new Separator());

                #region MapEVDic
                if (sobj != null)
                {
                    var mEditValue = new MenuItem { Header = "Edit MapEVDic" };
                    mEditValue.Click += new RoutedEventHandler((o, e) =>
                    {
                        ReceiveCmd("editMapEVDic", _key);
                    });
                    contextMenu.Items.Add(mEditValue);

                    var dDic = server.app.DataManager.GetMapObjEVDDic(App.DefineFile, sobj);
                    foreach (var evKey in sobj.EVDic.Keys)
                    {
                        var def = dDic[evKey];
                        var item = sobj.EVDic[evKey];
                        var miEV = new MenuItem
                        {
                            Header = new TextBlock { Text = evKey + " " + item.Version }
                        };

                        if (def.Type == typeof(comm.lib.MapEvent))
                        {
                            #region MapEvent
                            var subTypes = comm.lib.MapEvent.GetSubTypes();
                            foreach (var type in subTypes)
                            {
                                var type2 = type;
                                var tb = new TextBlock { Text = type.Name };
                                if (type.IsInstanceOfType(item.Value))
                                {
                                    tb.FontWeight = FontWeights.Bold;
                                }
                                var mi2 = new MenuItem { Header = tb };
                                mi2.Click += new RoutedEventHandler((o, e) =>
                                {
                                    item.Value = Activator.CreateInstance(type2);
                                    ReceiveCmd("fill");
                                });
                                miEV.Items.Add(mi2);
                            }
                            #endregion
                        }
                        else if (def.Type == typeof(comm.lib.MapRange))
                        {
                            #region MapRange
                            var subTypes = comm.lib.MapRange.GetSubTypes();
                            foreach (var type in subTypes)
                            {
                                var type2 = type;
                                var tb = new TextBlock { Text = type.Name };
                                if (type.IsInstanceOfType(item.Value))
                                {
                                    tb.FontWeight = FontWeights.Bold;
                                }
                                var mi2 = new MenuItem { Header = tb };
                                mi2.Click += new RoutedEventHandler((o, e) =>
                                {
                                    item.Value = Activator.CreateInstance(type2);
                                    ReceiveCmd("fill");
                                });
                                miEV.Items.Add(mi2);
                            }
                            #endregion
                        }
                        if (item.Value is comm.lib.MapEvent.Command)
                        {
                            #region Command
                            miEV.Items.Add(new Separator());
                            var cmd = (comm.lib.MapEvent.Command)item.Value;
                            var types = comm.lib.MapEvent.Command.GetCommandTypes();
                            foreach (var type in types)
                            {
                                var type2 = type;
                                var tb = new TextBlock { Text = type };
                                if (type == cmd.Type.ToString())
                                {
                                    tb.FontWeight = FontWeights.Bold;
                                }
                                var mi2 = new MenuItem { Header = tb };
                                mi2.Click += new RoutedEventHandler((o, e) =>
                                {
                                    cmd.Type = comm.EnumHelper.Parse<comm.lib.MapEvent.CommandType>(type2);
                                    ReceiveCmd("fill");
                                });
                                miEV.Items.Add(mi2);
                            }
                            #endregion
                        }
                        contextMenu.Items.Add(miEV);
                    }
                    contextMenu.Items.Add(new Separator());
                }
                #endregion

                #region mEditRPack
                var mEditRPack = new MenuItem { Header = "Edit RPack" };
                mEditRPack.Click += new RoutedEventHandler(mEditRPack_Click);
                contextMenu.Items.Add(mEditRPack);
                #endregion

                if (sobj != null)
                {
                    #region Edit DObj
                    var mEditDObj = new MenuItem { Header = "Edit DObj" };
                    mEditDObj.Click += new RoutedEventHandler((o, e) =>
                    {
                        ReceiveCmd("editDObj", _key);
                    });
                    contextMenu.Items.Add(mEditDObj);
                    #endregion
                }

                #region mDelete
                var mDelete = new MenuItem { Header = "Delete" };
                mDelete.Click += new RoutedEventHandler(mDelete_Click);
                contextMenu.Items.Add(mDelete);
                #endregion

                rectDot.ContextMenu = contextMenu;
            }
            #endregion

            if (_sprite != null)
            {
                #region fill els
                _elementDic.Clear();
                var elDic = _sprite.GetGroup(_spriteGroupKey).ElementDic;
                foreach (var key in elDic.Keys)
                {
                    var el = elDic[key];
                    if (el is comm.objst.RSprite.ILayer)
                    {
                        var layer = Layer.NewLayer(this, key, (comm.objst.RSprite.ILayer)el);
                        if (layer != null)
                        {
                            layer.CmdCallback = ReceiveCmd;
                            _elementDic.Add(key, layer);
                        }
                    }
                    else if (el is comm.objst.RSprite.Label)
                    {
                        var label = new LabelLayer(this, key, (comm.objst.RSprite.Label)el);
                        label.CmdCallback = ReceiveCmd;
                        _elementDic.Add(key, label);
                    }
                }
                #endregion
            }

            #region group name
            if (_sprite != null)
            {
                _gridRoot.Children.Add(new Border
                {
                    Background = new SolidColorBrush(sobj != null ? Color2.Yellow(50) : Color2.White(50)),
                    Child = new TextBlock
                    {
                        FontWeight = FontWeights.Bold,
                        Text = _sprite.GetGroup(_spriteGroupKey).Name
                    },
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Top
                });
            }
            #endregion

            #region IsExternal
            if (_mapObj != null && _mapObj.IsExternal)
            {
                _gridRoot.Children.Add(new Border
                {
                    Width = 16,
                    Height = 16,
                    CornerRadius = new CornerRadius(16),
                    Background = new SolidColorBrush(Color2.LightGreen(30)),
                    BorderBrush = new SolidColorBrush(Color2.LightGreen()),
                    BorderThickness = new Thickness(1)
                });
            }
            #endregion

            UpdateLayout();
        }
        public void UpdateLayout()
        {
            _gridRoot.SetLeftTop(
                LocX * PATH_SIZE - CenterSize / 2 + PATH_SIZE / 2,
                LocY * PATH_SIZE - CenterSize / 2 + PATH_SIZE / 2);

            foreach (var br in _brRangeList)
            {
                if (br.Tag is comm.lib.MapRange.Round)
                {
                    var roundRage = (comm.lib.MapRange.Round)br.Tag;
                    br.SetLeftTop(
                        (LocX - roundRage.Radius + roundRage.OffsetX) * PATH_SIZE,
                        (LocY - roundRage.Radius + roundRage.OffsetY) * PATH_SIZE);
                }
            }

            foreach (var el in _elementDic.Values)
            {
                el.UpdateLayout();
            }
        }
        public void SetShow(string[] keys)
        {
            var list = new List<string>(keys);
            foreach (var key in _elementDic.Keys)
            {
                if (_elementDic[key] is Layer)
                {
                    ((Layer)_elementDic[key]).IsShow = list.Contains(key);
                }
            }
        }
    }
}
